package YAPclient;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JTextField;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import YAPenvironment.YAPconstant;
import YAPenvironment.YAPconstant.Command;


/**
 * One of the two main windows the GUI consists of.
 * This window is opened right after the client has connected to a server. It shows information on all existing lobbies/games
 * as well as a list of all the players currently registered on this server. Furthermore, it allows users
 * to write and receive chat messages.
 */

public class FormLobbyOverview extends JFrame implements ListSelectionListener, ActionListener, KeyListener, MouseListener{

	private static final long serialVersionUID = 3840244881170603606L;
	
	private String sep = YAPconstant.C_SEPARATOR;

	private DefaultListModel lobbyListModel = new DefaultListModel();	
	private DefaultListModel playersOnServerListModel = new DefaultListModel();
	private DefaultListModel playersInLobbyListModel = new DefaultListModel();
	private DefaultListModel chatListModel = new DefaultListModel();
				
	private javax.swing.JButton btnCreateLobby;
	private javax.swing.JButton btnJoinLobby;
	private javax.swing.JButton btnSend;
	private javax.swing.JLabel lblLobby;
	private javax.swing.JLabel lblPlayground;
    private javax.swing.JLabel lblChat;
    private javax.swing.JLabel lblPlayersOnServer;
    private javax.swing.JLabel lblPlayersInLobby;
    private javax.swing.JScrollPane jspChatOutput;
    private javax.swing.JScrollPane jspLobby;
    private javax.swing.JScrollPane jspPlayersInLobby;
    private javax.swing.JScrollPane jspPlayersOnServer;
    private javax.swing.JList lstLobby;
    private javax.swing.JList lstPlayersOnServer;
    private javax.swing.JList lstChatOutput;
    private javax.swing.JList lstPlayersInLobby;
	private CreateLobbyDialog createLobbyDialog;
    private ChangeNameDialog changeNameDialog;
	private SoundDialog soundDialog;
    private LimitedTextField ltfChatInput;
    private Playground fPlayground;
    
	    	    
    /**
     * Creates a new form
     */
	public FormLobbyOverview(){
		
		 
		if (DataModul.clPlayer != null){	
			
			createGUI();
			
			addWindowListener(new ExitListener());
			setTitle("YAP: "+DataModul.clPlayer.getName());
			setIconImage(YAPconstant.ICONIMAGE);
			DataModul.parseThread.writeMessage(Command.getLobbyList.toString()+sep);
			
		}
	}
	
	
	 private void createGUI() {
		 
		 	/*
		 	 * Initialize Components
		 	 */
		 
	        lblPlayground = new javax.swing.JLabel("Playground:");
		 	fPlayground = new Playground(GameInformation.GameMode.lobbyOverview);
		 	
	        lblLobby = new javax.swing.JLabel("Lobbies:");
	        jspLobby = new javax.swing.JScrollPane();
	        lstLobby = new javax.swing.JList(lobbyListModel);
	        lstLobby.addMouseListener(this);
	        lstLobby.addListSelectionListener(this);
	        jspLobby.setViewportView(lstLobby);

	        btnCreateLobby = new javax.swing.JButton("Create");
	        btnCreateLobby.addActionListener(this);

	        btnJoinLobby = new javax.swing.JButton("Join");
	        btnJoinLobby.addActionListener(this);
	        
	        lblPlayersInLobby = new javax.swing.JLabel("Players In Lobby:");
	        jspPlayersInLobby = new javax.swing.JScrollPane();
	        lstPlayersInLobby = new javax.swing.JList(getPlayersInLobbyListModel());
	        lstPlayersInLobby.addMouseListener(this);
	        lstPlayersInLobby.addListSelectionListener(this);
	        jspPlayersInLobby.setViewportView(lstPlayersInLobby);

	        lblChat = new javax.swing.JLabel("Chat");
	        
	        jspChatOutput = new javax.swing.JScrollPane();
	        jspChatOutput.setPreferredSize(new Dimension(jspChatOutput.getPreferredSize().width+45,jspChatOutput.getPreferredSize().height));
	        lstChatOutput =  new javax.swing.JList(getChatListModel());
	        lstChatOutput.addMouseListener(this);
	        jspChatOutput.setViewportView(lstChatOutput);
	        jspChatOutput.setWheelScrollingEnabled(true);
	        
	        ltfChatInput = new LimitedTextField(new SetMaxText(50));
	        ltfChatInput.addKeyListener(this);
	        ltfChatInput.setFocusable(true);     
	        btnSend = new javax.swing.JButton("Chat");
	        btnSend.addActionListener(this);
	        
	        lblPlayersOnServer = new javax.swing.JLabel("Players on Server:");
	        jspPlayersOnServer = new javax.swing.JScrollPane();
	        lstPlayersOnServer = new javax.swing.JList(playersOnServerListModel);
	        lstPlayersOnServer.addMouseListener(this);
	        jspPlayersOnServer.setViewportView(lstPlayersOnServer);
	        
	        setJMenuBar(new YAPMenuBar(false));

	        /*
	         * Layout
	         */
	      
	        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
	        getContentPane().setLayout(layout);
	        layout.setHorizontalGroup(
	            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
	            .addGroup(layout.createSequentialGroup()
	                .addContainerGap()
	                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
	                    .addComponent(lblLobby)
	                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
	                        .addGroup(layout.createSequentialGroup()
	                            .addComponent(btnCreateLobby, javax.swing.GroupLayout.PREFERRED_SIZE, 86, javax.swing.GroupLayout.PREFERRED_SIZE)
	                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
	                            .addComponent(btnJoinLobby, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE))
	                        .addComponent(jspLobby, javax.swing.GroupLayout.PREFERRED_SIZE, 224, javax.swing.GroupLayout.PREFERRED_SIZE)))
	                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
	                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
	                    .addComponent(jspPlayersInLobby, javax.swing.GroupLayout.PREFERRED_SIZE, 297, javax.swing.GroupLayout.PREFERRED_SIZE)
	                    .addComponent(lblPlayersInLobby, javax.swing.GroupLayout.PREFERRED_SIZE, 300, javax.swing.GroupLayout.PREFERRED_SIZE)
	                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
	                        .addComponent(lblPlayground, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
	                        .addComponent(fPlayground, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
	                .addGap(21, 21, 21)
	                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
	                    .addComponent(lblPlayersOnServer, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
	                    .addComponent(jspChatOutput,javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE)
	                    .addComponent(jspPlayersOnServer, javax.swing.GroupLayout.PREFERRED_SIZE, 205, javax.swing.GroupLayout.DEFAULT_SIZE)
	                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
	                        .addComponent(ltfChatInput)
	                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
	                        .addComponent(btnSend, javax.swing.GroupLayout.PREFERRED_SIZE, 62, javax.swing.GroupLayout.PREFERRED_SIZE))
	                    .addComponent(lblChat, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
	                .addContainerGap())
	        );
	        layout.setVerticalGroup(
	            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
	            .addGroup(layout.createSequentialGroup()
	                .addGap(20, 20, 20)
	                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
	                    .addComponent(lblLobby)
	                    .addComponent(lblPlayground)
	                    .addComponent(lblChat))
	                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
	                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
	                    .addGroup(layout.createSequentialGroup()
	                        .addComponent(jspChatOutput)
	                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
	                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
	                            .addComponent(ltfChatInput, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE)
	                            .addComponent(btnSend, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE))
	                        .addGap(18, 18, 18)
	                        .addComponent(lblPlayersOnServer)
	                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
	                        .addComponent(jspPlayersOnServer, javax.swing.GroupLayout.PREFERRED_SIZE, 148, javax.swing.GroupLayout.PREFERRED_SIZE))
	                    .addGroup(layout.createSequentialGroup()
	                        .addComponent(jspLobby)
	                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
	                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
	                            .addComponent(btnCreateLobby)
	                            .addComponent(btnJoinLobby)))
	                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
	                        .addComponent(fPlayground, javax.swing.GroupLayout.DEFAULT_SIZE, 279, Short.MAX_VALUE)
	                        .addGap(10, 10, 10)
	                        .addComponent(lblPlayersInLobby)
	                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
	                        .addComponent(jspPlayersInLobby, javax.swing.GroupLayout.PREFERRED_SIZE, 138, javax.swing.GroupLayout.PREFERRED_SIZE)))
	                .addGap(29, 29, 29))
	        );

	        pack();  
	        setLocationRelativeTo(null);
	        setVisible(true);
	    }

	 
	 /**  
	  * Opens a {@link CreateLobbyDialog}
	  */
	 public void openCreateLobbyDialog() {
			createLobbyDialog = new CreateLobbyDialog(this);
		}
	 
	 
	 /**
	  * Closes the {@link CreateLobbyDialog} owned by this form (if existing)
	  */
	 public void closeCreateLobbyDialog() {
		 if(createLobbyDialog != null)
			 createLobbyDialog.dispose();
	 }
	 
	 
	 /**
	  * Opens a {@link ChangeNameDialog}
	  */
	 public void openChangeNameDialog() {
			changeNameDialog = new ChangeNameDialog(this);
		}
	 
	 
	 /**
	  * Closes the {@link ChangeNameDialog} owned by this form (if existing)
	  */
	 public void closeChangeNameDialog() {
		 if(changeNameDialog != null)
			 changeNameDialog.dispose();
	 }
	 
	 /**
	  * Opens a {@link SoundDialog}
	  */
	 @Deprecated
	 public void openSoundDialog() {
		soundDialog = new SoundDialog(this);		
	 }
		
	 
	 /**
	  * Closes the {@link SoundDialog} owned by this form (if existing)
	  */
	 @Deprecated
	 public void closeSoundDialog() {
		if(soundDialog != null)
			soundDialog.dispose();		
	 }

	 
	 /**
	  * Adds an error message to the currently opened dialog (either an instance of {@link CreateLobbyDialog} or {@link ChangeNameDialog})
	  * @param errMsg Error message to be displayed
	  */
	 public void setErrorMessage(String errMsg){
			if(createLobbyDialog != null && createLobbyDialog.isVisible())
				createLobbyDialog.setErrorMessage(errMsg);
			else if(changeNameDialog != null && changeNameDialog.isVisible())
				changeNameDialog.setErrorMessage(errMsg);
		}
		
	 
	/**
	 * Removes all elements from the lobby list
	 */
	public void clearLobbyList(){
		lobbyListModel.removeAllElements();
	}
	
	
	/**
	 * Removes all elements from the playersOnServer list
	 */
	public void clearPlayersOnServerList(){
		playersOnServerListModel.removeAllElements();
	}
	
	
	/**
	 *Removes all elements from the playersInLobby list 
	 */
	public void clearPlayersInLobbyList(){
		playersInLobbyListModel.removeAllElements();
	}
	
	
	/**
	 * Updates this form's playersInLobby list by first clearing it and then adding the names contained in the specified array
	 * @param players Array containing the names and states (ready/unready) of the players being added
	 */
	public void updatePlayersInLobbyList(String[] players){
		clearPlayersInLobbyList();
		for(int i=0; i<players.length-1; i+=2){
			String s = players[i];
			if (players[i+1].equalsIgnoreCase("true") && (!DataModul.flo.isVisible())){
				s= s + " (ready)";
			}
			playersInLobbyListModel.addElement(s);
		}
	}
	
	
	/**
	 * Updates this form's playersOnServer list by first clearing it and then adding the names contained in the specified array
	 * @param players Array containing the names of the players being added
	 */
	public void updatePlayersOnServerList(String[] players){
		clearPlayersOnServerList();
		for(int i=1; i<players.length;i++){
			playersOnServerListModel.addElement(players[i]);
		}
	}

	/**
	 * Updates this form's lobby list by first clearing it and then adding the names contained in the specified array
	 * @param lobbies Lobby information array containing the names and properties of the lobbies being added
	 */
	public void updateLobbyList(String[] lobbies){
		clearLobbyList();
		String[][] lobbyList = new String[lobbies.length][3];
	 	for(int i=0; i<lobbies.length;i++){
			lobbyList[i] = lobbies[i].split(sep);
			lobbyListModel.addElement(new GameInformation(lobbyList[i][0],Integer.parseInt(lobbyList[i][1]),Integer.parseInt(lobbyList[i][2])));
	 	}
	}
	
	
	/**
	 * Clears the chat history, removing all messages that have been written or received so far
	 */
	public void clearChatHistory() {
		chatListModel.removeAllElements();
	}


	/**
	 * @return this form's chatListModel, i.e. the list model which contains all chat messages that have been written or received
	 */
	public DefaultListModel getChatListModel() {
		return chatListModel;
	}

	
	/**
	 * @return This form's playersInLobbyListModel, i.e. the list model which contains all players in the currently selected lobby
	 */
	public DefaultListModel getPlayersInLobbyListModel() {
		return playersInLobbyListModel;
	}
	
	
	/**
	 * Sends the content of the chat input field to the server (using the {@link ParseThread}'s sendChatMsg method)
	 * @param strMsg The message that is being sent
	 */
	public void sendChat(String strMsg){
		try {
			DataModul.parseThread.sendChatMsg(ltfChatInput.getText());
			ltfChatInput.setText("");
		} catch (Exception e1) {
			System.out.println("Nachricht konnte nicht gesendet oder empfangen werden.");
		}
	}
	
	
	/**
	 * Adds a message to this form's chat history
	 * @param msg Chat message that is being added
	 */
	public void addChatMessage(String msg){
		chatListModel.add(chatListModel.getSize(),msg);
		jspChatOutput.validate();
		jspChatOutput.getVerticalScrollBar().setValue(jspChatOutput.getVerticalScrollBar().getMaximum());
		if(DataModul.fgl!=null){
			DataModul.fgl.adjustChatScrollBar();
		}

		
	}
	
	
	/**
	 * Sets the whisper command '/name' in the specified text field
	 * @param output Text field where the whisper command is to be set
	 * @param input List from where the user has chosen a name he wants to whisper to
	 */
	public static void setWhisper(JTextField output, JList input){
		try{
			String to = ((String)input.getSelectedValue()).split(":")[0];
			System.out.println(to);
			if(to.startsWith("(f")){
				to=to.substring(6, to.length());
				to=to.substring(0,to.length()-1);
			}else if(to.startsWith("(t")){
				to=to.substring(4, to.length());
				to=to.substring(0,to.length()-1);
			}
			output.setText("/"+to.split(" ")[0]+" ");
			output.grabFocus();
		}catch (Exception e){
			if(YAPconstant.C_DEBUG){ e.printStackTrace(); }
		}
	}

	
	/**
	 * Gives users the possibility to request information about a lobby by clicking on the corresponding entry in the lobby list
	 * @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent)
	 */
	@Override
	public void valueChanged(ListSelectionEvent e) {
		JList lst = (JList)e.getSource();
		if (lst.getSelectedValue() instanceof GameInformation){
			GameInformation tmp = ((GameInformation)lst.getSelectedValue());
			DataModul.gInfo.setName(tmp.getName());
			DataModul.gInfo.setCurrentPlayerCount(tmp.getCurrentPlayerCount());
			DataModul.gInfo.setMaxPlayerCount(tmp.getMaxPlayerCount());
			try{
				DataModul.parseThread.writeMessage(Command.getLobbyInformation.toString()+sep+DataModul.gInfo.getName());
			}
			catch( Exception e1){
			}
		}else if(lst.equals(lstLobby)&&lstLobby.getSelectedValue()==null){
			boolean found=false;
			for(Object o: lobbyListModel.toArray()){
				if(((GameInformation) o).getName().equals(DataModul.gInfo.getName())){
					lst.setSelectedValue(o,true);
					found =true;
				}
			}
			if(found==false){
			DataModul.gInfo.reset();
			getPlayersInLobbyListModel().removeAllElements();
			}
		}
		
	}

	
	/**
	 * This method is called from the Button "Connect" or Double-klick on a Lobby.
	 * It tries to join a Lobby.
	 */
	private void connectToLobby(){
		try{
			if (lstLobby.getSelectedValue() != null && lstLobby.getSelectedValue() instanceof GameInformation){
				GameInformation gi = (GameInformation)lstLobby.getSelectedValue();
				DataModul.gInfo.setName(gi.getName());
				DataModul.gInfo.setMaxPlayerCount(gi.getMaxPlayerCount());
				DataModul.parseThread.writeMessage(Command.joinLobby.toString()+sep+DataModul.gInfo.getName());
			}
		}catch(Exception exc){				
			if(YAPconstant.C_DEBUG){ exc.printStackTrace(); }
		}
	}
	
	/**
	 * Handles user input from the buttons btnCreateLobby, btnJoinLobby and btnSend
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	@Override
	public void actionPerformed(ActionEvent e) {
		
		if (e.getSource().equals(btnCreateLobby)){
			this.createLobbyDialog = new CreateLobbyDialog(this);
		}else if(e.getSource().equals(btnJoinLobby)){
			connectToLobby();
		}else if(e.getSource()==btnSend){
			sendChat(ltfChatInput.getText());
		}
		
	}


	/**
	 * Gives users the possibility to send chat messages by hitting ENTER (same result as clicking on btnSend)
	 * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
	 */
	@Override
	public void keyReleased(KeyEvent e) {
		if(e.getKeyCode()==KeyEvent.VK_ENTER){
			sendChat(ltfChatInput.getText());
		}
	}
	
	
	@Override
	public void keyTyped(KeyEvent e) {}
	@Override
	public void keyPressed(KeyEvent e) {}

	
	/**
	 * Gives users the possibility to select the player they want to whisper to
	 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
	 */
	@Override
	public void mouseClicked(MouseEvent evt) {
		if (evt.getClickCount() == 2) {
			if(evt.getSource()==lstPlayersOnServer){
				setWhisper(ltfChatInput, lstPlayersOnServer);				
			}
			else if(evt.getSource()==lstPlayersInLobby){
				setWhisper(ltfChatInput, lstPlayersInLobby);
			}
			else if(evt.getSource()==lstChatOutput){
				setWhisper(ltfChatInput, lstChatOutput);
			}else if(evt.getSource()==lstLobby){
				connectToLobby();
			}
		}
	}
	

	@Override
	public void mousePressed(MouseEvent arg0) {}
	
	
	@Override
	public void mouseEntered(MouseEvent arg0) {}
	
	@Override
	public void mouseExited(MouseEvent arg0) {}
	
	@Override
	public void mouseReleased(MouseEvent evt) {}

	
	/**
	 * This class handles window closing events received from instances of the class {@link FormLobbyOverview},
	 * i.e. disconnects the corresponding client
	 */
	class ExitListener extends WindowAdapter{
		public void windowClosing(WindowEvent event){
			DataModul.disconnect();
			System.out.println("Disconnected, shutting down...");
			System.exit(0);
		}
	}


	
}
